﻿using System;

namespace 值类型与引用类型
{
    class Program
    {
        static void Main(string[] args)
        {
            /*
              值类型：int ，long、float 、decimal、char、bool,struct 、 enum  (无符号的省掉了)
              值类型：只需要一段单独的内存，数据存放在 栈 中，随用随取随丢弃。

              引用类型：string 、集合（Array 、List）、class 、interface(接口)
              引用类型：需要2段内存，一段用于存真实的数据，一段存取数据的地址

              ref参数能够将一个变量带入一个方法进行改变，改变完成后，再将改变后的值带出方法。 

              ref参数要求在方法外必须为其赋值，而方法内可以不赋值。

              out参数要求在方法内部必须为其赋值

              ref参数和out参数的区别在于out参数可以在方法内部进行直接赋值方法外没有也没有关系，而ref参数方法外必须声明，如果不声明就会报错

              总结：

              1.ref参数方法外没有声明不可使用，可采用out参数。

              2.out 和 ref 都会把参数当成引用类型的形式传递，导致方法内对参数的修改会改变传值。

              3.out 可以不声明外部的变量 ，但是方法内的变更还是能影响到方法的外部，即便你是值类型
                参数的影响的方向：有里到外 （里：里面，方法体里面，外：方法体外）

              4.ref 不能由内至外，他是由外至内 ：外部必须先存在 （先声明）

              5.共同点：外部有变量，使用 ref out 都会改变这个变量，没有变量就使用out参数在方法内部声明变量并带出方法外

             */
            #region ref参数
            Console.WriteLine("----------有ref参数*-------");
            int age = 10;
            Console.WriteLine($"{nameof(Main)}-之前 age={age}");
            RefA(ref age);//传参数的时候必须写ref 
            Console.WriteLine($"{nameof(Main)}-之后 age={age}");//ref参数将参数带入方法进行改变并推出方法显示。
            //测试：没有ref参数
            Console.WriteLine("*-------没有ref参数----------");
            int age1 = 20;
            Console.WriteLine($"{nameof(Main)}-之前 age={age1}");
            RefB(age1);//没有改变，age方法内部和方法外部隔离，互不影响
            Console.WriteLine($"{nameof(Main)}-之后 age={age1}");
            #endregion

            #region out 参数
            //out参数可以不声明在方法内部声明 测试：
            Console.WriteLine("-------------有out参数-----------");
            OutA(out age);
            Console.WriteLine($"{nameof(Main)}内部声明后:{age}");//out 参数out 和 ref 都会把参数进行传递
            Console.WriteLine("------没有out参数-----");
            int ageb = 50;
            Console.WriteLine($"{nameof(Main)}之前:{ageb}");
            OutB(ageb);
            Console.WriteLine($"{nameof(Main)} 之后:{ageb}");
            #endregion

            #region 方法为引用类型
            Console.WriteLine("---string*----");
            string s = "小逼";
            Console.WriteLine($"{nameof(Main)}-之前-{s}");
            StrA(s);
            Console.WriteLine($"{nameof(Main)}-之后-{s}");
            //string 引用类型不可以影响到方法外部，而其他引用类型可以
            Console.WriteLine("---引用类型---");
            person a = new person { sex = "女生" };
            Console.WriteLine($"{nameof(Main)}-之前{a.sex}");//女生
            Per(a);//未知
            Console.WriteLine($"{nameof(Main)}-之后{a.sex}");//未知
            //结论：
            //1.跟 string 这种引用类型不一样：方法内的修改会影响到方法外部，但是 string 方法外不会被影响。
            //  把 string 的这种不同记住。
            //2.加上 ref 可以告诉方法：你不用执着的把参数以值类型传递
            //3.引用类型（非string）一定是会因为方法内的修改而改变。
            #endregion

        }
        public static void RefA(ref int age)//测试有ref参数方法
        {
            age = 20;
        }
        public static void RefB(int age)//测试没有ref参数方法
        {
            //不使用ref或out参数IDE会提醒你这是一个无用代码，因为不会传出方法外部
            age *= 30;
            Console.WriteLine(age);
        }
        public static void OutA(out int age)//测试有out参数方法
        {
            
             age = 70;
        }
        public static void OutB(int age)//测试没有out参数方法
        {
            age = 50;//声明的没有意义，传不出方法外部
            Console.WriteLine(age);
        }
        public static void StrA(string s)//测试string引用类型方法
        {
            s += "啊啊";
            Console.WriteLine(s);
        }
        public static void Per(person sex)//测试引用类型是否可以影响到方法外部
        {
            sex.sex = "未知";
            Console.WriteLine(sex.sex);
        }
    }
    class person//声明一个class类，引用类型
    {
        public string sex;
    }
}
